home *** CD-ROM | disk | FTP | other *** search
/ Deutsche Edition 1 / Deutsche Edition 1.iso / amok / 001-010 / amok05 / superlists1.2 / superlist.doc < prev    next >
Text File  |  1993-11-04  |  19KB  |  354 lines

  1. Dokumentation zu SuperLists Version 1.2
  2. Autor: Nicolas Benezan, Postwiesenstr. 2, D7000 Stuttgart 60
  3.  
  4. Einleitung
  5. ----------
  6. Jeder kennt die Windows mit den Proportionalgadgets im rechten und 
  7. unteren Rand. Man sieht sie auf der Workbench, in Textverarbeitungs- 
  8. programmen und -Editoren oder in Requestern bei der Auswahl einer zu 
  9. ladenden Datei (zB. bei Dpaint). Diese Windows oder Requester ermöglichen 
  10. eine komfortable Benutzung, weil man sich mit Hilfe der Scrollgadgets 
  11. jeden beliebigen Teil der Gesammtdaten anzeigen lassen kann, und man an 
  12. den Proportionalgadgets gleichzeitig ablesen kann, wie groß der 
  13. Ausschnitt ist, den man gerade sieht. Besonders nützlich ist dies, wenn 
  14. die Windowgröße veränderlich ist, und bei kleinem Window nicht mehr alle 
  15. Daten hineinpassen (siehe Workbench-Windows) oder, wie bei einem 
  16. Textverarbeitungsprogramm, eine Textseite von vorneherein viel größer als 
  17. eine Bildschirmseite ist.
  18. Wer selbst Programme schreibt, schreckt vielleicht vor der komplizierten 
  19. Programmierung solcher Scrollwindows zurück und verwendet lieber eine 
  20. einfacher zu programmierende Technik. Hierunter leidet die 
  21. Benutzerfreundlichkeit des Programms, und das muß wirklich nicht sein, 
  22. vor allem seit es "Superlists" gibt. Mit diesem Modul beschränkt sich der 
  23. Aufwand auf die Initialisierung und Verwaltung der Daten. Wie diese 
  24. angezeigt werden, wie gescrollt wird ist Sache von "SuperLists".
  25.  
  26. Neu: Version 1.2
  27. ----------------
  28. Version 1.0 hatte gegenüber dieser Version folgende Nachteile: es 
  29. verwendete immer das Modul Heap für seine Speicherverwaltung, eigene 
  30. Speicherverwaltungsroutinen einzubauen war sehr stressig. Außerdem war 
  31. das alte SuperLists auf vertikal und horizontal scrollende Listen 
  32. beschränkt.
  33. Bei der neuen Version kann auf Wunsch das horizontale Scrollgadget 
  34. weggelassen werden. Es ist nun auch möglich, mit Hilfe der 
  35. Prozedurvariablen AllocProc und DeallocProc eine eigene 
  36. Speicherverwaltung einzubinden.
  37. Es haben sich auch einige Wanzen in der Version 1.0 eingeschlichen, die 
  38. jetzt korrigiert sind:
  39. -RemoveEntry und InsertEntry haben die Liste nicht korrekt angezeigt, es 
  40. mußte jedesmal RefreshList aufgerufen werden
  41. -Hide bei obigen Prozeduren funktionierte nicht
  42. -ClickRow berechnete die Zeile falsch
  43.  
  44. So wird's gemacht
  45. -----------------
  46. Bevor man eine SuperList erzeugt, sollte man sich über folgendes im 
  47. Klaren sein:
  48. * In welchem Window(-rastport) oder Requester(-rastport) soll
  49.   die Liste angezeigt werden
  50. * Welche Hintergrundfarbe hat die Liste
  51. * Wie sollen die Proportionalgadgets für vertikales und (falls
  52.   erwünscht) horizontales Srolling aussehen
  53. * Wo und wie groß soll die Liste im Window (Requester)     angezeigt
  54.   werden
  55. * Welche Daten hat der zu dem verwendeten RastPort gehörige Zeichensatz
  56.   (Achtung, es darf keine Proportionalschrift sein)
  57. * Welcher Ausschnitt soll voreinstellungsgemäß angezeigt werden
  58. Neu: Bei der Version 1.2 kann nun auch gewählt werden, ob die Liste nur 
  59. vertikal oder vertikal und horizontal gescrollt werden soll.
  60. Falls jemand das Programm so verändern will, daß es auch Grafik (wie zB. 
  61. die Icons in den Workbench-Windows) beherrscht, bin ich gerne bereit, 
  62. eventuelle Fragen zu beantworten.
  63.  
  64. Erzeugen der Scrollgadgets
  65. --------------------------
  66. Jede SuperList benötigt ein oder zwei Proportionalgadgets, eines für das 
  67. vertikale Scrolling und optional ein zweites für die horizontale 
  68. Richtung. Diese Gadgets müssen erzeugt werden, bevor die Liste 
  69. initialisiert wird. Es wird hier auf eine Beschreibung, wie dies 
  70. geschieht, verzichtet. Dies kann man zB. im "Intuition Reference Manual" 
  71. nachlesen.
  72.  
  73. Initialisieren der Liste
  74. ------------------------
  75. Eine SuperList besteht aus einem Listenkopf (SuperList-RECORD) und null 
  76. oder mehreren Einträgen (Entry-RECORDs). Der Listenkopf enthält 
  77. Parameter, die das Aussehen der Liste beschreiben, und solche, die vom 
  78. SuperList-Modul zur Verwaltung der Liste benötigt werden, sowie eine 
  79. Exec.List zur Verwaltung der Einträge. Ein Eintrag enthält einen 
  80. Exec.Node zur verknüpfung der Einträge untereinander und den eigentlichen 
  81. Text (in Entry.node.name) sowie Parameter, die das Aussehen des Textes 
  82. beschreiben (Farbe, DrawMode).
  83. Bevor die Liste das erste mal verwendet werden kann, müssen folgende 
  84. Paramter in der SuperList-Struktur initialisiert werden:
  85. * rastPort - Zeiger auf den RastPort des Windows oder Requesters, in
  86.   dem die Liste angezeigt werden soll.
  87. * backPen - Hintergrundfarbe der Liste
  88. * propY - Zeiger auf das Proportionalgadget für vertikales Scrolling
  89. * propX - Zeiger auf das Gadget für horizontales Scrolling    
  90.   falls dies nicht erwünscht ist, sollte PropX auf NIL gesetzt werden
  91. * leftEdge - linker Rand der Listenanzeigefläche relativ zum linken
  92.   Rand des Windows oder Requesters (Einheit: Pixel)
  93. * topEdge - oberer Rand relativ zum Rand des Windows/Requesters
  94. * width - Breite der Anzeigefläche (Einheit: Pixel), muß mindestens so
  95.   groß wie FontXsize+1 sein (s. unten)
  96. * height - Höhe der Anzeigefläche, muß mindestens FontYsize sein
  97. * FontXsize - Breite eines Zeichens in Pixel (kein Proportional-
  98.   schriftsatz!)
  99. * FontYsize - Höhe einer Textzeile in Pixel (muß nicht unbedingt mit
  100.   der Höhe eines Zeichens übereinstimmen, wenn zB. zusätzlicher
  101.   Zeilenabstand erwünscht ist)
  102. * FontBaseLine - besagt, auf welcher Höhe die Grundlinie der Zeichen
  103.   innerhalb einer Zeile liegen soll, sollte ungefähr gleich dem
  104.   baseLine-Paramter des Fonts gesetzt werden
  105. * dispRow - Nummer des ersten Eintrags (Zeile) des zuerst    
  106.   anzuzeigenden Ausschnitts (0 ist der erste Eintrag)
  107. * dispColumn - Nummer der ersten Spalte des Ausschnitts (0 zeigt ab
  108.   der ersten Spalte) ist kein horizontales Scrolling vorgesehen, sollte
  109.   dieses Feld immer 0 sein
  110. * Die Exec.List muß initialisiert werden. Dies geschieht am besten mit
  111.   ExecSupport.NewList(ADR(SuperList.list); .
  112. Alle anderen Felder sollten nie verändert werden, sie dienen der internen 
  113. Verwaltung. Es ist aber vielleich manchmal interessant, sie zu lesen, 
  114. deshalb hier ihre Bedeutungen:
  115. * rows - Anzahl der vorhandenen Einträge (Zeilen)
  116. * columns - Anzahl der Spalten = Länge des längsten Eintrags (in
  117.   Zeichen, nicht in Pixel), nur bei eingeschltetem horizontalem
  118.   Scrolling definiert
  119. * topEntry - Zeiger auf den Node des obersten angezeigten Eintrags
  120. * bottomEntry - Zeiger auf den untersten angezeigten Eintrag
  121. (Vorsicht: Falls keine Einträge vorhanden sind, müssen top- und 
  122. bottomEntry nicht unbedingt NIL sein. Diese Zeiger sollten möglichst 
  123. nicht für eigene Zwecke verwendet werden.)
  124. * effWidth - Breite der Anzeigefläche in Zeichen (nicht in Pixel)
  125. * effHeight - Höhe in Zeichen
  126. Nachdem alles ordnungsgemäß initialisiert ist, wird RethinkList( 
  127. SuperListPtr, NewList) aufgerufen, wobei SuperListPtr ein Zeiger auf den 
  128. SuperList-RECORD und NewList eine von SuperLists exportierte Konstante 
  129. ist. Um den aktuellen Ausschnitt der Liste anzuzeigen, verwendet man 
  130. RefreshList(SuperListPtr). Man kann die Liste jetzt mit den Prozeduren 
  131. ScrollList, InsertEntry, RemoveEntry, GetEntry, SetProp, GetProp, 
  132. ClickRow, MakeEntry, RedrawEntry, RethinkList und RefreshList bearbeiten.
  133.  
  134. RefreshList
  135. -----------
  136. Diese Prozedur wird benötigt, um die Paramter zur interenen Verwaltung im 
  137. Listenkopf neu zu berechnen. Normalerweise werden diese beim Aufruf von 
  138. Prozeduren wie zB. InsertEntry automatisch aktualisiert. Es ist jedoch 
  139. manchmal nötig, einige Parameter "von Hand" zu ändern, wenn zB. der 
  140. Benutzer die Windowgröße verändert hat, und sich damit auch die 
  141. Anzeigefläche der Liste verändert. Um das SuperLists-Modul davon in 
  142. Kenntnis zu setzen, daß Parameter verändert wurden, sodaß es seine 
  143. internen Parameter neu berechnen kann, wird RethinkList verwendet. Der 
  144. Parameter New (newList oder oldList) besagt, inwieweit die 
  145. Listenparameter rekonstruiert werden müssen. RethinkList(...,oldList) 
  146. wird verwendet, wenn die Veränderung nur die Anzeigefläche betrifft (zB. 
  147. Höhe, Breite, Zeichensatz usw.). Die Ausführungszeit ist im Gegensatz zu 
  148. RethinkList(...,newList) relativ kurz. Bei letzterem wird nämlich die 
  149. ganze Liste der Einträge durchgegangen und Zeilen und Spalten werden neu 
  150. gezählt. Dies ist notwendig, wenn die Einträge mit anderen Prozeduren als 
  151. die von SuperLists bearbeitet wurden, sodaß sich die Texte der Einträge 
  152. verändert haben, oder Eiträge entfernt oder hinzugefügt wurden. Ein 
  153. weiterer Fall ist das Erzeugen einer neuen Liste (siehe oben) oder ein 
  154. Spezialfall von RemoveEntry (siehe dort). Wegen der besonders bei sehr 
  155. großen Listen langen Ausführungszeit sollte RethinkList(...,newList) 
  156. möglichst vermieden werden. Um einen einzelnen Eintrag abzuändern, ist es 
  157. besser, ihn mit RemoveEntry zu entfernen und nach der Änderung mit 
  158. InsertEntry wieder einzufügen.
  159. Folgende Parameter dürfen verändert werden, ohne daß RethinkList benötigt 
  160. wird:
  161. SuperList.backPen, SuperList.FontBaseLine, Entry.frontPen, Entry.back- 
  162. Pen, Entry.drawMode, Entry.userFlags, Entry.userData .
  163. Achtung: Wenn Parameter (außer den oben genannten) einer SuperList-
  164. Struktur "von Hand", also nicht über SuperLists-Prozeduren, verändert 
  165. werden, so darf keine andere SuperLists-Prozedur aufgerufen werden, bevor 
  166. nicht RethinkList ausgeführt wurde. Die Prozedur findet sonst ungültige 
  167. Werte vor, was zu einer chaotischen Anzeige und schlimmstenfalls sogar 
  168. zum Systemabsturz führen kann. Falls von mehreren Tasks aus auf eine 
  169. Liste zugegriffen wird, muß das Programmstück von der 
  170. Parameterveränderung bis RethinkList im Forbidden-Status ablaufen.
  171. RethinkList korrigiert soweit als möglich ungültige Werte. Wenn zB. der 
  172. aktuelle Anzeigeauschnitt über das Ende der Liste hinaus positioniert 
  173. wurde, so wird der Ausschnitt auf die tiefstmögliche Position gesetzt. 
  174. Diese Gutmütigkeit hat aber ihre Grenzen. So sollte man zB. keine Zeiger 
  175. der Exec.List "verbiegen" oder eine negative Anzeigenbreite einstellen, 
  176. weil solche "Scherze" meist unvorhersehbare Folgen haben.
  177.  
  178. RefreshList
  179. -----------
  180. Wie schon der Name sagt, dient diese Prozedur dem Auffrischen, dh. dem 
  181. Neu-Anzeigen, des sichtbaren Listenausschnitts. Dies wird in drei Fällen 
  182. benötigt:
  183. * wenn die Liste zum ersten mal angezeigt werden soll,
  184. * wenn sich die Liste in einem SimpleRefresh-Window befindet und man 
  185.   eine Message vom Typ "refreshWindow" erhält oder
  186. * wenn sich die Größe der Anzeigefläche verändert hat. Es ist auch 
  187.   dann ein Auffrischen erforderlich, wenn die Fläche kleiner geworden 
  188.   ist.
  189. Im Fall 1 und 3 ist auch noch zusätzlich SetProp() erforderlich (siehe 
  190. dort).
  191.  
  192. RedrawEntry
  193. -----------
  194. Zeigt einen Eintrag (Zeile) neu an, sofern sich dieser im gerade 
  195. sichtbaren Ausschnitt befindet. Dies ist notwendig, wenn zB. der Text, 
  196. die Farbe oder der DrawModus dieses Eintrags verändert wurde. Falls sich 
  197. die Länge des Texts verändert hat muß außerdem RethinkList() aufgerufen 
  198. werden (siehe oben). Es ist dabei möglich, vorher einen alternativen 
  199. Zeichensatz (zB. kursiv) zu setzen (mit Graphics.Set- Font() ), um diesen 
  200. Eintrag besonders hervorzuheben. Dieser muß (!) aber die gleiche 
  201. Zeichengröße wie der vorher verwendete haben (FontXsize, FontYsize).
  202.  
  203. ScrollList
  204. ----------
  205. wird verwendet, um die Liste Zeichenweise in eine beliebige Richtung zu 
  206. scrollen. Dies wird besonders bei Texteditoren benötigt, oder wenn die 
  207. beiden Proportionalscrollgadgets noch mit je zwei Boolgadgets kombiniert 
  208. sind (siehe zB. die kleinen Pfeile in den Ecken der Workbench-Windows). 
  209. Das Scrolling läuft relativ schnell (weil blitterunterstützt) ab, und es 
  210. wird nur der beim Verschieben der BitPlane-Daten freigelegte Abschnitt 
  211. neu gezeichnet. Es wird damit möglich, Texteditoren zu bauen, bei denen 
  212. mann nicht ewig warten muß, wenn man sich mit dem Cursor über den 
  213. Windowrand hinausbewegt hat (sowas soll's auch geben!).
  214. Es ist nicht möglich, über das Ende oder vor den Anfang der Liste zu 
  215. scrollen. Dies wird automatisch erkannt und abgefangen.
  216. "Dir" gibt die Richtung an, mit der sich das imaginäre Fenster über die 
  217. Liste bewegt. "Left" bedeutet also, die sichtbaren Daten werden 
  218. eigentlich rechtsverschoben, links wird ein Zeichen mehr sichtbar, rechts 
  219. verschwindet eins.
  220.  
  221. MakeEntry
  222. ---------
  223. Bevor irgendetwas angezeigt oder gescrollt werden kann, müssen erst 
  224. einmal Einträge in der Liste vorhanden sein. Meistens werden diese 
  225. dynamisch alloziert, was am besten mit der Prozedur MakeEntry geschieht. 
  226. Es wird ein Zeiger auf einen auf Null endenden String, Vorder- und 
  227. Hinergrundfarbe und der gewünschte DrawModus übergeben. MakeEntry 
  228. alloziert dann Speicher für den Entry-RECORD und den Text (einschließlich 
  229. abschließender Null), kopiert den Text in den neuen Speicher und 
  230. initialisiert den Entry-RECORD. Dabei wird die Länge des Textes (ohne 
  231. Null) im Feld node.pri abgelegt. Will man Einträge anders als mit 
  232. MakeEntry erzeugen, so ist es wichtig, dies ebenfalls zu tun, da die 
  233. Länge für die weitere Verwaltung der Liste nötig ist.
  234. Die Felder userFlags und userData werden nicht benutzt und können vom 
  235. Programmierer für beliebige Zwecke verwendet werden.
  236. Ist nicht mehr genügend freier Speicher vorhanden, wird NIL 
  237. zurückgegeben.
  238.  
  239. InsertEntry
  240. -----------
  241. wird verwendet, um an der Stelle (Zeile) "Row" den Eintrag "Entry" in die 
  242. Liste einzufügen. der Parameter "hidden" besagt, ob der Einfügevorgang 
  243. sichtbar (s. Konstante "showIt") oder unsichtbar (hideIt) erfolgen soll. 
  244. Dies kann wünschenswert sein, wenn mehere Einträge auf einmal eingefügt 
  245. werden sollen. Es wäre umständlich und würde eventuell zu viel Zeit 
  246. benötigen, nach jeder eingefügten Zeile die Liste neu anzuzeigen. 
  247. Stattdessen erfolgt das einfügen der Zeilen unsichtbar. Erst die letzte 
  248. Zeile wird sichtbar eingefügt, womit die gesammte Veränderung sichtbar 
  249. wird. Einfügen außerhalb des gerade sichtbaren Ausschnitts der Liste 
  250. erfolgt natürlich immer unsichtbar, unabhängig vom Parameter "hidden".
  251. Wird bei "Row" ein negativer Wert oder ein Wert, der hinter dem Ende der 
  252. Liste liegt, angegeben, dann wird am Ende der Liste angehängt.
  253.  
  254. RemoveEntry
  255. -----------
  256. Diese Prozedur entfernt einen Eintrag aus der Liste. "Row" bezeichnet die 
  257. Nummer des zu ertfernenden Eintrags (Zeile). Der Parameter "hidden" wird 
  258. wie bei InsertEntry gehandhabt. Mit dem Parameter "mustRethink" hat es 
  259. folgendes auf sich:
  260. Wird der längste Eintrag aus der Liste entfernt, ändert sich die 
  261. Gesammtbreite der Liste, und diese muß mit RethinkList neu bestimmt 
  262. werden. Dies wird nicht automatisch durchgeführt, da es Fälle gibt, in 
  263. denen das Aufrufende Programm besser weiß, wann dies nötig ist und wann 
  264. nicht. Es ist zB. denkbar, daß bei einem Textverarbeitungs- programm alle 
  265. Zeilen einer Seite sowieso die gleiche maximale Länge haben (DIN A4), und 
  266. es deshalb Performance-Verschwendung wäre, jedesmal RethinkList 
  267. durchzuführen. Wird also bei RemoveEntry festgestellt, daß der entfernte 
  268. Eintrag der Längste war, dann wird "mustRethink" TRUE, ansonsten bleibt 
  269. es unverändert. Das bedeutet zwar, daß dieses Flag vor dem (ersten von 
  270. mehreren) Aufruf(en) gelöscht werden muß, ermöglicht aber eine Art Oder-
  271. Verknüpfung bei mehreren RemoveEntry-Aufrufen hintereinander.
  272.  
  273. GetEntry
  274. --------
  275. ermöglicht es, die Adresse des Entrys mit der Numer "Row" zu suchen. 
  276. Falls der Eintrag vorhanden ist, wird ein Zeiger auf ihn zurückgegeben, 
  277. ansonsten ist das Ergebnis NIL.
  278. Es wird darauf hingewiesen, daß diese Suche unter Umständen lange dauern 
  279. kann, falls der gesuchte Eintrag nicht im sichtbaren Ausschnitt liegt 
  280. (dort wird zuerst gesucht). Besonders bei sehr großen Listen (deutlich 
  281. mehr als 1000 Einträge) kann es länger dauern, weil dann die gesammte 
  282. Liste durchgegangen wird. Dies gilt im übrigen auch für Insert- und 
  283. RemoveEntry, die intern auch diese Prozedur verwenden.
  284. Ich glaube aber, daß dies kein wesentliche Nachteil ist, weil Superlists 
  285. wohl meistens für weniger lange Listen verwendet werden wird.
  286.  
  287. ClickRow
  288. --------
  289. Mit dieser Prozedur kann festgestellt werden, welchen Eintrag der 
  290. Benutzer gemeint hat, als er einen Punkt im Window der Liste angeklickt 
  291. hat. Es werden die Koordinaten des Zeigers relativ zum Nullpunkt des 
  292. RastPorts (des Windows oder Requesters) übergeben. Das Ergebnis ist dann 
  293. die Nummer des Eintrags oder -1, falls sich der Mauszeiger außerhalb des 
  294. Anzeigebereichs der Liste befand.
  295.  
  296. GetProp
  297. -------
  298. Wenn festgestellt worden ist, daß der Benutzer eines der 
  299. Proportionalgadgets bewegt hat, muß die Stellung der Gadgets ausgelesen 
  300. und der gewünschte Ausschnitt der Liste dargestellt werden. GetProp 
  301. erledigt ersteres, RefreshList letzteres. Die beiden Prozeduren wurden 
  302. nicht zu einer zusammengefaßt, weil es möglich ist, daß der Programmierer 
  303. zwischen beiden noch einiges ausführen möchte. Bei der Verwendung von 
  304. Proportionalgadgets können nämlich Rundungsfehler auftreten, die sich 
  305. darin äußern, daß beim Vor- oder Zurückblättern um eine Seite (Anklicken 
  306. der Fläche neben dem Knob im Container) eine Zeile irrtümlicherweise 
  307. übersprungen wird. So ist es möglich, vor RefreshList die Parameter des 
  308. List-RECORDS entsprechend zu korrigieren (RethinkList nicht vergessen). 
  309. Wird darauf verzichtet, ist RethinkList nicht notwendig, dh. RefreshList 
  310. kann direkt nach GetProp aufgerufen werden.
  311.  
  312. SetProp
  313. -------
  314. Hat sich die Größe der Liste oder des Windows verändert, oder wurde mit 
  315. ScrollList gearbeitet, dann müssen Stellung (Pot) und Größe (Body) der 
  316. (Auto-)Knobs der Proportionalgadgets aktualisiert werden. Dies kann mit 
  317. der Prozedur SetProp geschehen, die die Pot- und Body-Variablen der 
  318. Gadgets entsprechend dem Verhältnis der sichtbaren Daten zu den 
  319. Gesammtdaten einstellt und danach Intuition.ModifyProp() aufruft. Sind 
  320. weniger Einträge vorhanden, als das Window hoch ist bzw. alle Einträge 
  321. kürzer sind als das Window breit ist, so werden die Body-Werte der 
  322. zugehörigen Gadgets auf das Maximum gestellt, dh. der Knob füllt den 
  323. ganzen Container.
  324.  
  325. AllocProc, DeallocProc
  326. ----------------------
  327. Wie auch schon in IntuiStruct 1.3 (Amok #3) wurde jetzt auch in 
  328. SuperLists die Möglichkeit geschaffen, andere Speicherverwaltungs- 
  329. routinen als die von Heap zu installieren. Sie können jetzt die Vorteile 
  330. einer eigenen Speicherverwaltung nutzen, wie sie z.B. "MemSystem" (auch 
  331. auf dieser Amok-Diskette) bietet.
  332. Die Prozedurvariablen AllocProc und DeallocProc müssen vor der Benutzung 
  333. von
  334.  
  335. * MakeEntry(...) oder
  336. * RemoveEntry(...,deallocIt)
  337.  
  338. initialisiert werden. Beispiel:
  339.  
  340. SuperLists.AllocProc:=MemSystem.Allocate;
  341. SuperLists.DeallocProc:=MemSystem.Deallocate;
  342.  
  343. oder
  344.  
  345. SuperLists.AllocProc:=Heap.Allocate;
  346. SuperLists.DeallocProc:=Heap.Deallocate;
  347.  
  348. ______________________
  349.  
  350. Viel Spaß !
  351.  
  352. Bene
  353.  
  354.